/*
 *    Copyright 2021 FightingGuys Team
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package cn.fightingguys.kaiheila;

import cn.fightingguys.kaiheila.config.SdkConfigurer;
import cn.fightingguys.kaiheila.config.api.ApiConfigurer;
import cn.fightingguys.kaiheila.config.event.EventSourceConfigurer;
import cn.fightingguys.kaiheila.entity.Guild;
import cn.fightingguys.kaiheila.event.EventListener;
import cn.fightingguys.kaiheila.event.EventManager;
import cn.fightingguys.kaiheila.event.EventSource;
import cn.fightingguys.kaiheila.event.source.WebSocketEventSource;
import cn.fightingguys.kaiheila.event.source.WebhookEventSource;
import cn.fightingguys.kaiheila.request.HttpRequestHeaderInterceptor;
import cn.fightingguys.kaiheila.request.Requester;
import okhttp3.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * KHL 内核实现类
 *
 * @author running_hyk
 * @version 1.0.0
 * @since 1.0.0
 */
public class KhlKernel implements KHL {

    private Status kernelStatus = Status.INITIALIZING;

    // Config
    private String baseUrl;
    private String apiToken;
    private boolean webhookMode;

    private final EventSource eventSource = new WebSocketEventSource(this);
    private final EventManager eventManager = new EventManager(this);

    private final OkHttpClient httpClient;
    private final Requester requester;

    private final HashMap<String, Guild> cacheGuilds = new HashMap<>();

    public KhlKernel(SdkConfigurer sdkConfigurer, ApiConfigurer apiConfigurer, EventSourceConfigurer eventSourceConfigurer) {
        configAllSettings(sdkConfigurer, apiConfigurer, eventSourceConfigurer);
        this.httpClient = buildOkhttp3Client();
        this.requester = new Requester(baseUrl);
//        logon();
    }

    private void configAllSettings(SdkConfigurer sdkConfigurer, ApiConfigurer apiConfigurer, EventSourceConfigurer eventSourceConfigurer) {
        this.baseUrl = apiConfigurer.getBaseUrl();
        this.apiToken = apiConfigurer.getToken();
        this.webhookMode = eventSourceConfigurer.isWebhookMode();
    }

    private OkHttpClient buildOkhttp3Client() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        return builder
                .connectionSpecs(Collections.singletonList(ConnectionSpec.RESTRICTED_TLS))
                .connectionPool(new ConnectionPool())
                .followSslRedirects(false)
                .retryOnConnectionFailure(true)
                .connectTimeout(8, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .addInterceptor(new HttpRequestHeaderInterceptor(apiToken))
                .build();
    }

    private void setKernelStatus(Status kernelStatus) {
        this.kernelStatus = kernelStatus;
    }

    @Override
    public Status getKernelStatus() {
        return kernelStatus;
    }

    public OkHttpClient getHttpClient() {
        return this.httpClient;
    }

    private void logon() {
        setKernelStatus(Status.LOGGING_IN);
        // Rest API GetSelfUser


        // Create Event Source
        EventSource eventSource;
        if (this.webhookMode) {
            eventSource = new WebhookEventSource(this);
        } else {
            eventSource = new WebSocketEventSource(this);
        }
//        eventManager.setEventSource(eventSource);
    }

    @Override
    public void awaitStatus(Status status) {
        while (status.ordinal() != kernelStatus.ordinal())
            Thread.yield();
    }

    @Override
    public void awaitReady() {
        awaitStatus(Status.READY);
    }

    @Override
    public void addEventListener(EventListener listener) {
        eventManager.addEventListener(listener);
    }

    @Override
    public void removeEventListener(EventListener listener) {
        eventManager.removeEventListener(listener);
    }

    @Override
    public List<EventListener> getRegisteredListeners() {
        return eventManager.getListeners();
    }
}
